#region Copyright & License
/*
   Copyright 2009-2010 Stepan Adamec (adamec@yasas.org)

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License. 
 */
#endregion

namespace See.Sharper {
  using System.IO;
  using System.Text;
  using System.Xml;
  using System.Xml.Serialization;

  #region Interfaces

  /// <summary>
  /// Generic interface for implementing various object serializers.
  /// </summary>
  /// <typeparam name="S">Type of the persistent storage.</typeparam>
  public interface ISerializer<S> {
    /// <summary>
    /// Reads the object of type T from the <paramref name="storage"/> of type S.
    /// </summary>
    /// <typeparam name="T">Type of the expected object.</typeparam>
    /// <param name="storage">Persistent storage where the objects are serialized.</param>
    /// <returns>Deserialized instance of the object.</returns>
    T Read<T>(S storage);

    /// <summary>
    /// Writes the object of type T to the <paramref name="storage"/> of type S using the default encoding (if applicable).
    /// </summary>
    /// <typeparam name="T">Type of the serialized object.</typeparam>
    /// <param name="storage">Persistent storage where the objects are serialized.</param>
    /// <param name="instance">Object to be serialized.</param>
    /// <returns>The same object instance.</returns>
    T Write<T>(S storage, T instance);

    /// <summary>
    /// Writes the object of type T to the <paramref name="storage"/> of type S using the given encoding.
    /// </summary>
    /// <typeparam name="T">Type of the serialized object.</typeparam>
    /// <param name="storage">Persistent storage where the objects are serialized.</param>
    /// <param name="instance">Object to be serialized.</param>
    /// <param name="encoding">Encoding used for serialization.</param>
    /// <returns>The same object instance.</returns>
    T Write<T>(S storage, T instance, Encoding encoding);
  }

  /// <summary>
  /// Generic interface for implementing serializers using files as a persistent storage.
  /// </summary>
  public interface IFileSerializer : ISerializer<FileInfo> { }

  #endregion

  #region XML serialization

  /// <summary>
  /// Object serializer using files as a persistent storage and XML as a serialization format.
  /// </summary>
  public class XmlFileSerializer : IFileSerializer {
    #region ISerializer<FileInfo> Members

    /// <summary>
    /// Reads the object of type T from the XML document stored in a file.
    /// </summary>
    /// <typeparam name="T">Type of the expected object.</typeparam>
    /// <param name="storage">Storage file.</param>
    /// <returns>Deserialized instance of the object.</returns>
    public T Read<T>(FileInfo storage) {
      using (XmlTextReader reader = new XmlTextReader(storage.FullName)) {
        try {
          return (T)new XmlSerializer(typeof(T)).Deserialize(reader);
        } finally {
          reader.Close();
        }
      }
    }

    /// <summary>
    /// Writes the object of type T to the XML document stored in a file using the default encoding.
    /// </summary>
    /// <typeparam name="T">Type of the serialized object.</typeparam>
    /// <param name="storage">Storage file.</param>
    /// <param name="instance">Object to be serialized.</param>
    /// <returns>The same object instance.</returns>
    public T Write<T>(FileInfo storage, T instance) {
      return Write<T>(storage, instance, Encoding.Default);
    }

    /// <summary>
    /// Writes the object of type T to the XML document stored in a file using the given <paramref name="encoding"/>.
    /// </summary>
    /// <typeparam name="T">Type of the serialized object.</typeparam>
    /// <param name="storage">Storage file.</param>
    /// <param name="instance">Object to be serialized.</param>
    /// <param name="encoding">Encoding used for serialization.</param>
    /// <returns>The same object instance.</returns>
    public T Write<T>(FileInfo storage, T instance, Encoding encoding) {
      using (XmlTextWriter writer = new XmlTextWriter(storage.FullName, encoding)) {
        try {
          writer.Formatting = Formatting.Indented;

          new XmlSerializer(typeof(T)).Serialize(
            writer, instance
          );
        } finally {
          writer.Close();
        }
      }

      return instance;
    }

    #endregion

    #region XmlFileSerializer Members

    /// <summary>
    /// Reads the object of type T from the XML document stored in a file. File is specified with a string <paramref name="path"/>.
    /// </summary>
    /// <typeparam name="T">Type of the expected object.</typeparam>
    /// <param name="path">Path to the storage file.</param>
    /// <returns>Deserialized instance of the object.</returns>
    public T Read<T>(string path) {
      return Read<T>(new FileInfo(path));
    }

    /// <summary>
    /// Writes the object of type T to the XML document stored in a file using the default encoding. File is specified with a string <paramref name="path"/>.
    /// </summary>
    /// <typeparam name="T">Type of the serialized object.</typeparam>
    /// <param name="path">Path to the storage file.</param>
    /// <param name="instance">Object to be serialized.</param>
    /// <returns>The same object instance.</returns>
    public T Write<T>(string path, T instance) {
      return Write<T>(new FileInfo(path), instance);
    }

    /// <summary>
    /// Writes the object of type T to the XML document stored in a file using the given <paramref name="encoding"/>. File is specified with a string <paramref name="path"/>.
    /// </summary>
    /// <typeparam name="T">Type of the serialized object.</typeparam>
    /// <param name="path">Path to the storage file.</param>
    /// <param name="instance">Object to be serialized.</param>
    /// <param name="encoding">Encoding used for serialization.</param>
    /// <returns>The same object instance.</returns>
    public T Write<T>(string path, T instance, Encoding encoding) {
      return Write<T>(new FileInfo(path), instance, encoding);
    }

    #endregion
  }

  #endregion
}
